package com.chat;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.ProgressMonitor;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.text.BadLocationException;


public class Client {

	private ChatFrame frame;
	private LoginFrame loginframe;
	private Chat tmpChat;//临时
	private boolean isConnected = false;

	

	private Socket socket;// 连接服务器
	private PrintWriter writer;
	private BufferedReader reader;
	private MessageThread messageThread;// 负责接收服务器消息的线程
	//private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有在线用户
	private String myIP = "";


	private boolean isPressed = false;//ctrl+enter 的判断
	private Map<String,Chat> chatMap = new HashMap<String, Chat>();//聊天框
	private Map<String,SendFileThread> sendfileMap = new HashMap<String, SendFileThread>(); //发送文件线程集合
	private SimpleDateFormat sdf =null;
	// 主方法,程序入口
	public static void main(String[] args) throws BindException {
		new Client();

	}

	public Client() throws BindException {
		try {
			myIP = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		initLoginFrame();
		
		initChatFrame();
		sdf = new SimpleDateFormat("（HH:mm:ss）：");
	}

	
	private void initLoginFrame(){
		//登陆窗口
				loginframe = new LoginFrame();
				
				loginframe.getBtn_submit().addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						login();
					}
				});
				loginframe.getTxt_login_name().addKeyListener(new KeyAdapter() {
					@Override
					public void keyPressed(KeyEvent e) {
						// TODO Auto-generated method stub
						if(e.getKeyCode()==KeyEvent.VK_ENTER){
							login();
						}
					}
				});
				loginframe.getBtn_submit().addKeyListener(new KeyAdapter() {
					@Override
					public void keyPressed(KeyEvent e) {
						// TODO Auto-generated method stub
						if(e.getKeyCode()==KeyEvent.VK_ENTER){
							login();
						}
					}
				});
				// 关闭窗口时事件
				loginframe.addWindowListener(new WindowAdapter() {
					public void windowClosing(WindowEvent e) {
						if (isConnected) {
							closeConnection();// 关闭连接
						}
						System.exit(0);// 退出程序
					}
					
				});
	}
	
	
	private void initChatFrame(){
		//会话框
				frame = new ChatFrame();
				
				frame.getUserList().addMouseListener(new MouseAdapter() {
	        		@Override
	        		public void mouseClicked(MouseEvent e) {
	        			// TODO Auto-generated method stub
	        			if(e.getClickCount()==2){
	        				
	        				String msg = "" + frame.getListModel().getElementAt(frame.getSendfor_who());
	        		        StringTokenizer stringTokenizer = new StringTokenizer(
	        		                msg, "()");
	        		        String user_name = stringTokenizer.nextToken();
	        		        createChat(user_name);    
	        			}
	        		}
				});
				

				// 单击断开按钮时事件
				frame.getBtn_stop().addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						if (!isConnected) {
							JOptionPane.showMessageDialog(frame, "已处于断开状态，不要重复断开!", "错误", JOptionPane.ERROR_MESSAGE);
							return;
						}
						try {
							boolean flag = closeConnection();// 断开 服务器
							
							if (flag == false) {
								throw new Exception("断开连接发生异常！");
							}
							JOptionPane.showMessageDialog(frame, "成功断开!");
							frame.setVisible(false);
							frame.getTextArea().setText("");
							loginframe.setVisible(true);
							frame.getListModel().removeAllElements();
							
							//消除所有聊天框
							for(Entry<String, Chat> en:chatMap.entrySet()){
								Chat cht = en.getValue();
								cht.dispose();
							}
							
						} catch (Exception exc) {
							JOptionPane.showMessageDialog(frame, exc.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
						}
					}
				});

				// 关闭窗口时事件
				frame.addWindowListener(new WindowAdapter() {
					public void windowClosing(WindowEvent e) {
						if (isConnected) {
							closeConnection();// 关闭连接
							
						}
						System.exit(0);// 退出程序
					}
				});
	}
	
	/**
     * 创建或显示聊天框
     */
    private Chat createChat(String user_name){
    	
        
		Chat c = chatMap.get(user_name);
		if(c!=null){
			//System.out.println("**********************");
			c.setVisible(true);
		}else{
			c = new Chat();
            c.setTitle(user_name);
            tmpChat=c;
            chatMap.put(user_name, c);
            //发送文字内容
            c.getB_insert().addActionListener(new ActionListener() { 
    			public void actionPerformed(ActionEvent e) {
    				sendText(tmpChat);
    			}
    		});
            //发送文件
            c.getB_file().addActionListener(new ActionListener() { 
    			public void actionPerformed(ActionEvent e) {
    				sendFile(tmpChat);
    			}
    		});
            //触发发送
            c.getAddText().addKeyListener(new KeyAdapter() {
    			public void keyPressed(KeyEvent e) {
    				if (KeyEvent.VK_CONTROL == e.getKeyCode()) {
    					isPressed = true;
    				}
    				if (e.getKeyCode() == KeyEvent.VK_ENTER && isPressed) {
    					sendText(tmpChat);
    				}
    			}

    			public void keyReleased(KeyEvent e) {
    				if (KeyEvent.VK_CONTROL == e.getKeyCode() || KeyEvent.VK_ENTER == e.getKeyCode()) {
    					isPressed = false;
    				}
    			}
    		});
		}
		return c;
		
    }
	
    private void sendText(Chat c){
    	
    	String message = c.getFontAttrib().getText().trim();
		if (message == null || message.trim().equals("")) {
			JOptionPane.showMessageDialog(frame, "消息不能为空！", "错误", JOptionPane.ERROR_MESSAGE);
			return;
		}

			
		String user_name = c.getTitle();
		String from_user = frame.getTitle();
		try {
			//本地聊天框显示
			c.getDoc().insertString(c.getDoc().getLength(), "你"+sdf.format(new Date())+"\n", c.getFontAttrib().getUserAttrSet());
			c.getDoc().insertString(c.getDoc().getLength(), message +"\n", c.getFontAttrib().getAttrSet());
		} catch (BadLocationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		c.getAddText().setText("");
		message = message.replaceAll("[\\r\\n]", "`");//转换回车换行
		sendMessage("SENDMSG#" + user_name + "#" +from_user + "#" + message);
			
    }
	private void sendFile(Chat c){
		// 文件选择对话框启动，当选择了文件以后给每一个client发送文件
		JFileChooser sourceFileChooser = new JFileChooser(".");

		sourceFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		int status = sourceFileChooser.showOpenDialog(c);
		if (sourceFileChooser.getSelectedFile() == null) {
			return;
		}
		if (status == JFileChooser.CANCEL_OPTION) {
			return;
		}
		File sourceFile = new File(sourceFileChooser.getSelectedFile().getPath());

		String fileName = sourceFile.getName();// 文件名
		
		String user_name = c.getTitle();
	

		if (status == JFileChooser.APPROVE_OPTION) {
			
			ServerSocket ss=null;
			try {
				ss = new ServerSocket(0);
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			
			SendFileThread snd = new SendFileThread(c, ss, sourceFile, sourceFileChooser, status);
			snd.start();
			sendfileMap.put(user_name, snd);//放入集合
			
			int p = ss.getLocalPort();//传送端口
			
			//等待1秒后，发文件信息
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			// 请求服务器，发送文件
			sendMessage("FILE#" + p + "#" + fileName + "#" + String.valueOf(sourceFile.length()) + "#"
						+ user_name + "#" + frame.getTitle() + "#" + myIP);

			
			//本地聊天框显示
			try {
				c.getDoc().insertString(c.getDoc().getLength(), "你"+sdf.format(new Date())+"\n", c.getFontAttrib().getUserAttrSet());
				c.getDoc().insertString(c.getDoc().getLength(), " 发送一个文件:" + fileName +"\n", c.getFontAttrib().getDefaultAttrSet());
			} catch (BadLocationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	
	}
	
	private void login(){

		
		int port;
		String message_name = loginframe.getTxt_login_name().getText().trim();
		if (message_name == null || message_name.equals("")) {
			JOptionPane.showMessageDialog(loginframe.getLogPanle(), "昵称不能为空！", "错误", JOptionPane.ERROR_MESSAGE);
			return;
		}
		
		if (isConnected) {// 登陆成功之前，应该不会连接服务器
			String message1 = loginframe.getTxt_login_name().getText().trim();
			if (message1 == null || message1.equals("")) {
				JOptionPane.showMessageDialog(loginframe.getLogPanle(), "昵称不能为空！", "错误", JOptionPane.ERROR_MESSAGE);
				return;
			}
			
			sendMessage("USERLOGIN#" + message1  + "#" + myIP);
			return;
		} else {
			try {
				try {
					port = Integer.parseInt(loginframe.getTxt_login_port().getText().trim());
				} catch (NumberFormatException e2) {
					throw new Exception("端口号不符合要求!");
				}
				String hostIp = loginframe.getTxt_login_ip().getText().trim();
				String name = loginframe.getTxt_login_name().getText().trim();
				if (name.equals("") || hostIp.equals("")) {
					throw new Exception("昵称、服务器IP不能为空!");
				}
				// 连接服务器
				boolean flag = connectServer(port, hostIp, name);
				if (flag == false) {
					throw new Exception("与服务器连接失败!");
				}
				frame.setTitle(name);
			
			} catch (Exception exc) {
				JOptionPane.showMessageDialog(loginframe, exc.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
				return;
			}
		}
		
		sendMessage("USERLOGIN#" + message_name + "#" + myIP);
	
	}
	

	/**
	 * 连接服务器
	 *
	 * @param port
	 * @param hostIp
	 * @param name
	 */
	public boolean connectServer(int port, String hostIp, String name) {
		// 连接服务器
		try {
			socket = new Socket(hostIp, port);// 根据端口号和服务器ip建立连接
			writer = new PrintWriter(socket.getOutputStream());
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			// 发送客户端用户基本信息(用户名和ip地址,文件服务器端口)
			sendMessage(name + "#" + myIP);
			// 开启接收消息的线程
			messageThread = new MessageThread(reader, frame.getTextArea());
			messageThread.start();
			System.out.println("连接上服务!");
			isConnected = true;// 已经连接上了
			return true;
		} catch (Exception e) {
			//frame.getTextArea().append("与端口号为：" + port + "    IP地址为：" + hostIp + "   的服务器连接失败!" + "\r\n");
			isConnected = false;// 未连接上
			return false;
		}
	}
	
	/**
	 * 向服务器 发送消息
	 *
	 * #param message
	 */
	public void sendMessage(String message) {
		writer.println(message);
		writer.flush();
	}

	/**
	 * 客户端主动关闭连接
	 */
	@SuppressWarnings("deprecation")
	public synchronized boolean closeConnection() {
		try {
			sendMessage("CLOSE");// 发送断开连接命令给服务器
			messageThread.stop();// 停止接受消息线程

			// 释放资源
			if (reader != null) {
				reader.close();
			}
			if (writer != null) {
				writer.close();
			}
			if (socket != null) {
				System.out.println("主动断开服务器");
				socket.close();
			}
			isConnected = false;
			frame.getListModel().removeAllElements();
			return true;
		} catch (IOException e1) {
			e1.printStackTrace();
			isConnected = true;
			return false;
		}
	}


	// 不断接收服务器消息的线程
	class MessageThread extends Thread {
		private BufferedReader reader;
		private JTextArea textArea;

		// 接收消息线程的构造方法
		public MessageThread(BufferedReader reader, JTextArea textArea) {
			this.reader = reader;
			this.textArea = textArea;
		}

		// 被动的关闭连接
		public synchronized void closeCon() throws Exception {
			// 清空用户列表
			frame.getListModel().removeAllElements();
			// 被动的关闭连接释放资源
			if (reader != null) {
				reader.close();
			}
			if (writer != null) {
				writer.close();
			}
			if (socket != null) {
				socket.close();
				System.out.println("服务器已关闭");
			}
			isConnected = false;// 修改状态为断开

		}

		public void run() {
			String message = "";
			while (true) {
				try {
					message = reader.readLine();
					System.out.println("服务器反馈:" + message);
					StringTokenizer stringTokenizer = new StringTokenizer(message, "#");
					String command = stringTokenizer.nextToken();// 命令
					if (command.equals("CLOSE")) { // 服务器已关闭命令
						
						closeCon();// 被动的关闭连接
						JOptionPane.showMessageDialog(frame, "服务器已关闭！", "错误", JOptionPane.ERROR_MESSAGE);
						frame.setVisible(false);
						
						loginframe.setVisible(true);
						
						//消除所有聊天框
						for(Entry<String, Chat> en:chatMap.entrySet()){
							Chat cht = en.getValue();
							cht.dispose();
						}
						
						return;// 结束线程
					} else if (command.equals("USERLOGIN")) {// 登陆
						String st = stringTokenizer.nextToken();
						if (st.equals("OK")) {
							JOptionPane.showMessageDialog(loginframe, "登陆成功!");
							loginframe.setVisible(false);
							frame.setVisible(true);
							
							frame.getTxt_name().setText(loginframe.getTxt_login_name().getText());
							frame.getTxt_name().setEnabled(false);// 设置不可用
							
							frame.getTxt_port().setText(loginframe.getTxt_login_port().getText());
							frame.getTxt_port().setEnabled(false);
							
							frame.getTxt_hostIp().setText(loginframe.getTxt_login_ip().getText());
							frame.getTxt_hostIp().setEnabled(false);
							

						} else if (st.equals("NAMELOGIN")) {
							
							if (isConnected) {
								try {
									sendMessage("CLOSE");// 发送断开连接命令给服务器
									// 释放资源
									if (reader != null) {
										reader.close();
									}
									if (writer != null) {
										writer.close();
									}
									if (socket != null) {
										System.out.println("主动断开服务器");
										socket.close();
									}
									isConnected = false;
									frame.getListModel().removeAllElements();

								} catch (IOException e1) {
									e1.printStackTrace();
									isConnected = true;
								}
							}
							JOptionPane.showMessageDialog(loginframe, "已有该昵称登陆，请换昵称!");
							return;
						} else {
							
							if (isConnected) {
								try {
									sendMessage("CLOSE");// 发送断开连接命令给服务器
									// 释放资源
									if (reader != null) {
										reader.close();
									}
									if (writer != null) {
										writer.close();
									}
									if (socket != null) {
										System.out.println("主动断开服务器");
										socket.close();
									}
									isConnected = false;
									frame.getListModel().removeAllElements();

								} catch (IOException e1) {
									e1.printStackTrace();
									isConnected = true;
								}
								
							}
							JOptionPane.showMessageDialog(loginframe, "该IP已有用户登陆，不能重复登陆!");
							return;
						}

					} 
					/*else if (command.equals("ADD")) {// 有用户上线更新在线列表
						String username = "";
						String userIp = "";
						if ((username = stringTokenizer.nextToken()) != null
								&& (userIp = stringTokenizer.nextToken()) != null) {
							User user = new User(username, userIp);
							onLineUsers.put(username, user);
							frame.getListModel().addElement(username);
						}
					} else if (command.equals("DELETE")) {// 有用户下线更新在线列表
						String username = stringTokenizer.nextToken();
						User user = (User) onLineUsers.get(username);
						onLineUsers.remove(user);
						frame.getListModel().removeElement(username);
					} */
					else if (command.equals("USERLIST")) {// 加载在线用户列表
						frame.getListModel().removeAllElements();
						
						StringTokenizer strToken;
						
						int size = Integer.parseInt(stringTokenizer.nextToken());
						String username = null;
						for (int i = 0; i < size ; i++) {
							username = stringTokenizer.nextToken();
							strToken = new StringTokenizer(username, "()");
							if (strToken.nextToken().equals(frame.getTitle())) {
								continue;
							} else {
								frame.getListModel().addElement(username);
							}

						}
					} else if (command.equals("MAX")) {// 人数已达上限
						
						if (isConnected) {
							try {
								sendMessage("CLOSE");// 发送断开连接命令给服务器
								// 释放资源
								if (reader != null) {
									reader.close();
								}
								if (writer != null) {
									writer.close();
								}
								if (socket != null) {
									System.out.println("主动断开服务器");
									socket.close();
								}
								isConnected = false;
								frame.getListModel().removeAllElements();

							} catch (IOException e1) {
								e1.printStackTrace();
								isConnected = true;
							}
							
						}
						JOptionPane.showMessageDialog(frame, "服务器人数已达上限！", "错误", JOptionPane.ERROR_MESSAGE);
						return;// 结束线程
						// 连接发送者，接受文件
					}else if (command.equals("LEAVE")) {//传送文件时,对方已离线
						String toUser = stringTokenizer.nextToken();
						Chat c=createChat(toUser);
						SendFileThread snd = sendfileMap.get(toUser);
						if(snd!=null&&snd.isAlive()){
							snd.stop();//强制关闭
						}
						JOptionPane.showMessageDialog(c, "对方已离线！", "错误", JOptionPane.ERROR_MESSAGE);
						
						// 连接发送者，接受文件
					} else if (command.equals("FILE")) {
						int portNumber = Integer.parseInt(stringTokenizer.nextToken());
						String fileName = stringTokenizer.nextToken();
						long fileSize = Long.parseLong(stringTokenizer.nextToken());
						String Nickname = stringTokenizer.nextToken();//发送者
						String ip = stringTokenizer.nextToken();
						
						Chat c=createChat(Nickname);
						
						String userInfo = "你"+sdf.format(new Date());
						Socket fileSenderSocket = null;
						try {
							fileSenderSocket = new Socket(ip, portNumber);
						} catch (IOException ex) {
							JOptionPane.showMessageDialog(c, "无法连接到服务器接收文件!", "错误", JOptionPane.ERROR_MESSAGE);
						}
						//启动接受文件线程
						new AcceptFileThread(fileSenderSocket, c, fileName, fileSize, userInfo).start();

					}else if (command.equals("NOTICE")) {//服务器通知 
						String mess = stringTokenizer.nextToken();
						//转换消息内容
						String[] strarr=mess.split("\\`");
						String tmp ="";
						for(int i=0;i<strarr.length;i++){
							tmp+=strarr[i]+"\r\n";
						}
						textArea.append("服务器通知："+"\n"+tmp);
					}else {// 普通消息
						String sender = stringTokenizer.nextToken();//发送者
						
						Chat c = createChat(sender);
						
						String mess = stringTokenizer.nextToken();
						//转换消息内容
						String[] strarr=mess.split("\\`");
						String tmp ="";
						for(int i=0;i<strarr.length;i++){
							tmp+=strarr[i]+"\r\n";
						}
						//本地聊天框显示,接受的
						c.getDoc().insertString(c.getDoc().getLength(), sender+sdf.format(new Date())+"\n", c.getFontAttrib().getUserAttrSet());
						c.getDoc().insertString(c.getDoc().getLength(), tmp, c.getFontAttrib().getDefaultAttrSet());
						
					}
				} catch (IOException e) {
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}